#pyhk.py
#Extends pyhook to have normal hotkey functionality like autohotkey (AHK) scripts
#Hotkeys have to be entered in lists! Even if single items.
#Hotkeys can be entered in ID list or human readble.
#Example: human readble: ['Lcontrol','7'] for CTRL 7

import pythoncom, pyHook, ctypes, pickle

class pyhk:
    """Hotkey class extending pyhook"""

    def __init__(self):
        #initiate internal hotkey list
        self.KeyDownID = []
        #initiate user hotkey list
        self.UserHKF = []
        #End hotkey = CTL Shift Q
        self.EndHotkey = [162,160,81]

        #initiate Key2ID and ID2Key look up
        fKey2ID = open('Key2ID', 'r')
        pickleKey2ID = fKey2ID.read()
        self.Key2ID = pickle.loads(pickleKey2ID)
        fKey2ID.close()


        fID2Key = open('ID2Key', 'r')
        pickleID2Key = fID2Key.read()
        self.ID2Key = pickle.loads(pickleID2Key)
        fID2Key.close()
        
        # create a hook manager
        self.hm = pyHook.HookManager()
        # watch for all mouse events
        self.hm.KeyDown = self.OnKeyDown
        self.hm.KeyUp = self.OnKeyUp
        # set the hook
        self.hm.HookKeyboard()

    def start(self):
        """Start pyhk to check for hotkeys"""
        pythoncom.PumpMessages()

    def end(self):
        """End pyhk to check for hotkeys"""
        ctypes.windll.user32.PostQuitMessage(0)

    def isIDHotkey(self,hotkey):
        """Test if hotkey is coded in IDs"""
        try:
            [self.ID2Key[key] for key in hotkey]
        except:
            return False
        return True

    def isHumanHotkey(self,hotkey):
        """Test if hotkey is coded human readable. Ex ALT F2"""
        try:
            [self.Key2ID[key] for key in hotkey]
        except:
            return False
        return True

    def hotkey2ID(self,hotkey):
        """Converts human readable hotkeys to IDs"""
        if self.isHumanHotkey(hotkey):
            return [self.Key2ID[key] for key in hotkey]
        else:
            raise Exception("Invalid Hotkey")

            
    def addHotkey(self,hotkey,fhot):
        """Add hotkeys with according function"""
        if self.isIDHotkey(hotkey):
            self.UserHKF.append([hotkey,fhot])
        else:
            self.UserHKF.append([self.hotkey2ID(hotkey),fhot])
            
    def removeHotkey(self,hotkey):
        """Add hotkeys with according function"""
        tempUserHKF = [[hotk, fun] for hotk, fun in self.UserHKF if not(hotk == hotkey)]
        self.UserHKF = tempUserHKF[:]

    def addEndHotkey(self, hotkey):
        self.EndHotkey = hotkey[:]        

    def isHotkey(self,hotkey):
        """Check if hotkey is pressed down
            Hotkey is given as KeyID"""

        try:            
            #make sure exact hotkey is pressed
            if not(len(hotkey) == len(self.KeyDownID)):
                return  False
            for hotk in hotkey:
                if not(hotk in self.KeyDownID):
                    return False
        except:
            return False
        
        return True

    def OnKeyDown(self,event):

        #make sure key only gets presse once
        if not(event.KeyID in self.KeyDownID):
            self.KeyDownID.append(event.KeyID)

            #Add user hotkeys and functions
            for hk, fun in self.UserHKF:
                if self.isHotkey(hk):
                    fun()
            
            #End loop
            if self.isHotkey(self.EndHotkey):
                self.end()

        return True

     
    def OnKeyUp(self,event):
        self.KeyDownID.remove(event.KeyID)
        return True

if __name__ == '__main__':
    
    import time

    hot = pyhk()

    #test ALT 1
    def funk1():
        print "ALT 1"

    hot.addHotkey([164,49],funk1)

    #test ctrl = 162 shift = 160 3 = 51
    def funk2():
        print "CTRL Shift 3"

    hot.addHotkey([162,160,51],funk2)

    #test ctrl = 162 shift = 160 3 = 51
    def funk3():
        print "CTRL 7"

    hot.addHotkey(['Lcontrol','7'],funk3)

    print "starting pyhk phase"
   
    hot.start()

##    #change end hotkey to 6
##    hot.addEndHotkey([54])
##
##    #remove hotkey ALT F2
##    hot.removeHotkey([164,113])
##
##    print "reentering pyhk phase"
##
##    hot.start()
##
##    def funk3():
##        
##        hot.end()
##
##    hot.addHotkey([51],funk3)
##
##    print "special exit function, rereentering pyhk phase"
##
##    hot.start()

